home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 2 / Mac Magazin and MacEasy Magazine CD - Issue 02.iso / Sharewarebibliothek / Applikationen / Alpha.5.81 folder / Tcl / UserCode / MacPerl.tcl < prev    next >
Text File  |  1994-06-12  |  16KB  |  528 lines

  1. #############################################################################
  2. # MacPerl.tcl
  3. #
  4. # This is a set of routines that allow Alpha to act as a front end for the
  5. # standalone MacPerl application and that allows Perl scripts to be used as 
  6. # text filters in Alpha.
  7. #
  8. #     !!!  This package requires Alpha version 5.75 or higher  !!!
  9. #
  10. #############################################################################
  11. # Features:
  12. #
  13. # A Perl menu is created that allows the following actions:
  14. #
  15. # 1.  Selected text (or the entire buffer) may be interpreted as a Perl 
  16. #     script by MacPerl.
  17. #    
  18. # 2.  A selected Perl script file may be executed by MacPerl.
  19. #
  20. # 3.  Perl scripts that read from standard input and write to standard
  21. #     output my be used as text filters within Alpha.  
  22. #
  23. #     A submenu of "preattached" Perl scripts is constructed from the 
  24. #     contents of a "Text Filters" folder within the main MacPerl folder.
  25. #     This folder will be created if it doesn't already exist.
  26. #
  27. #     Besides the "preattached" filters, a disk file or an Alpha buffer
  28. #     which contains a Perl script may be chosen as the text filter.
  29. #     (The latter option allows simple one-time scripts to be created and 
  30. #     applied on the fly.  This can be very useful because, even
  31. #     with the overhead to start up MacPerl, large-scale global search-
  32. #     and-replace operations (hundreds of replaces) are substantially faster
  33. #     in MacPerl than in Alpha.)
  34. #
  35. #     The output of Perl text filters may be chosen to overwrite the 
  36. #     selected Alpha text or else written into a new window.  (Everything 
  37. #     is undoable, in any case).
  38. #
  39. #     The filter may be applied either to the currently selected text
  40. #     or to the entire buffer.
  41. #
  42. # 4.  The temporary i/o files used by the text filter mechanism may be 
  43. #     examined.
  44. #
  45. # 5.  The Perl menu may be rebuilt, in case files are added or removed 
  46. #     from the "Text Filters" folder.
  47. #
  48. #############################################################################
  49. # Installation:
  50. #
  51. # This file must be placed in the folder where you keep local Tcl 
  52. # procedures.  The following lines should be added to your 
  53. # 'userStartup.tcl' file (in the Alpha home directory), with the 
  54. # appropriate path names for your own system.
  55. #     set macperlPath "Macintosh HD:Programming:MacPerl ƒ:MacPerl"
  56. #     source  ":Tcl:Local:MacPerl.tcl"
  57. #
  58. # A sampling of useful :-) Perl scripts are included in the folder "Text Filters".
  59. # You should drag this folder into your MacPerl folder, where MacPerl.tcl will
  60. # look for it.  The "Text Munging" scripts are largely from the Nutshell book 
  61. # ("Programming Perl") and the s2p script is my adaptation of the standard script 
  62. # that converts Unix "sed" scripts to Perl.  The others may be useful examples, 
  63. # as well.
  64. #
  65. # ...........................................................................
  66. #
  67. # If you don't already have MacPerl, it's available by anonymous ftp from
  68. # a number of sites, of which the most accessible seem to be
  69. #
  70. #  grind.isca.uiowa.edu [128.255.21.233]   
  71. #                                 mac/umich/development/languages/macperl4.12.sit
  72. #
  73. #  nic.switch.ch [130.59.1.40]    software/mac/perl/Mac_Perl_412_appl.sit.bin
  74. #
  75. #############################################################################
  76. # Authors: W. Thomas Pollard (pollard@cucbs.chem.columbia.edu)
  77. #          Martijn Koster (m.koster@nexor.co.uk)
  78. #
  79. # Version History:
  80. #
  81. # 0.7  3/94 WTP -  nested Text Filters folder now supported
  82. #                  menu format modified somewhat
  83. # 0.6  3/94 WTP -  'applyToBuffer' flag added
  84. #                  scripts in Alpha buffers can now be used as filters 
  85. # 0.5  2/94 WTP -  'filters', 'open special' submenu added
  86. #                  'overwrite' flag added
  87. # 0.2  1/94 MK  -  menu support added
  88. #                  'execute selection', 'execute buffer' commands added
  89. # 0.1  9/93 WTP -  text filter functionality created
  90. #                  
  91. #############################################################################
  92.  
  93. global perlMenu macperlPath perlOverwrite perlUsebuffer
  94.  
  95. set perlOverwrite 1
  96. set perlUsebuffer 1
  97.  
  98. #############################################################################
  99. #  Return paths to standard files, based on the path to MacPerl:
  100. #  (This should make it easier to move MacPerl, install new versions,
  101. #  etc., without breaking the scripts.
  102. #
  103. proc macperlFolder {} {
  104.    global macperlPath
  105.    regexp {(.*):([^:]*)} $macperlPath pathname dirname filename
  106.    return ${dirname}:
  107. }
  108.  
  109. proc stdinPath {} {
  110.    return [macperlFolder]STDIN
  111. }
  112.  
  113. proc stdoutPath {} {
  114.    return [macperlFolder]STDOUT
  115. }
  116.  
  117. proc scriptPath {} {
  118.    return [macperlFolder]SCRIPT
  119. }
  120.  
  121. proc scriptFolder {} {
  122.    return "[macperlFolder]Text Filters:"
  123. }
  124.  
  125. #############################################################################
  126. #  Set the "overwrite" flag.  If true, then the output of a Perl filter
  127. #  is inserted in place of the originally selected text.  Otherwise, it is
  128. #  placed in a new window.  The names of the routines reflect the condition
  129. #  of the flag _before_ the routine is called, so that the menu makes more
  130. #  sense.
  131. proc •OverwriteSelection {} {
  132.     global perlOverwrite
  133.     set perlOverwrite 0
  134.     rebuildPerlMenu
  135. }
  136.  
  137. proc •Don\'tOverwriteSelection {} {
  138.     global perlOverwrite
  139.     set perlOverwrite 1
  140.     rebuildPerlMenu
  141. }
  142.  
  143. #############################################################################
  144. #  Set the "usebuffer" flag.  If true, then the Perl filter is applied to 
  145. #  the entire buffer.  Otherwise, only the selected text is filtered.
  146. proc •ApplyToBuffer {} {
  147.     global perlUsebuffer
  148.     set perlUsebuffer 0
  149.     rebuildPerlMenu
  150. }
  151.  
  152. proc •ApplyToSelection {} {
  153.     global perlUsebuffer
  154.     set perlUsebuffer 1
  155.     rebuildPerlMenu
  156. }
  157.  
  158. #############################################################################
  159. #  This is a generally useful proc that builds a hierarchical menu 
  160. #  from the files in a given folder and all subfolders.  As the menu is
  161. #  built, the pathnames of the various files are saved in the array
  162. #  indicated  by $filePaths.  The index of the file's path in this array
  163. #  is formed by concatenating the submenu name and filename, allowing the
  164. #  pathname to be retrieved by the procedure $proc when the menu item is
  165. #  selected.
  166. #
  167. proc buildSubMenu {folder name proc filePaths} {
  168.     global $filePaths
  169.     if {$name == 0} {
  170.         set name [file tail [file dirname $folder]]
  171.     }
  172.     if {$proc == 0} {
  173.         set pproc ""
  174.     } else {
  175.         set pproc "-p $proc"
  176.     }
  177.     set menu {}
  178.     set filenames [glob -nocomplain  $folder\*]
  179.     if {[llength $filenames] > 0} {
  180.        foreach m $filenames {
  181.           if {[file isdirectory $m]} {
  182.               lappend menu [buildSubMenu ${m}: 0 $proc $filePaths] 
  183.           } elseif {[file isfile $m]} {
  184.               set fname [file tail $m]
  185.               lappend menu $fname
  186.               set ${filePaths}($name:$fname) $m
  187.           }
  188.          }
  189.     }
  190.     return [concat {menu -m -n} [list $name] $pproc [list $menu]]
  191. }
  192.  
  193. #############################################################################
  194. #  Build a submenu of "preattached" Perl filters using the names of the 
  195. #  scripts in the Text Filters directory
  196. #
  197. proc perlFilterMenu {} {
  198.     global perlFilterPath HOME
  199.     set scriptDir [scriptFolder]
  200.     if {![file exists $scriptDir]} {
  201.        cpdir "$HOME:Tcl:UserCode:Text Filters" [macperlFolder]
  202.        alertnote "Created \"[macperlFolder]Text Filters\" folder."
  203.     }
  204.     return [buildSubMenu $scriptDir TextFilters perlExecuteFilter perlFilterPath]
  205. }
  206.  
  207. proc rebuildPerlMenu {} {
  208.     global perlMenu perlOverwrite perlUsebuffer
  209.     
  210.     if {$perlOverwrite} {
  211.             set overwriteItem •OverwriteSelection 
  212.         } else {
  213.             set overwriteItem •Don\'tOverwriteSelection 
  214.         }
  215.     
  216.     if {$perlUsebuffer} {
  217.             set usebufferItem •ApplyToBuffer 
  218.         } else {
  219.             set usebufferItem •ApplyToSelection 
  220.         }
  221.     
  222.     menu -n $perlMenu [ concat {
  223.         "macperl"
  224.         "(-"
  225.         "runTheSelection"
  226.         "runTheBuffer"
  227.         "runAFile"
  228.         "(-"
  229.         } [list [perlFilterMenu]] {
  230.         {menu -n OtherTextFilters {
  231.            "selectABuffer"
  232.            "selectAFile"
  233.            }
  234.         } 
  235.         } $overwriteItem  {
  236.         } $usebufferItem  {
  237.         "(-"
  238.         {menu -m -n openSpecial -p perlOpenFile {
  239.            "STDIN"
  240.            "STDOUT"
  241.            "SCRIPT"
  242.            }
  243.         } 
  244.         "(-"
  245.         "rebuildPerlMenu"
  246.         } ]
  247.  
  248.     removeMenu $perlMenu
  249.     insertMenu $perlMenu
  250. }
  251.  
  252. rebuildPerlMenu
  253.  
  254. #############################################################################
  255. # Switch to MacPerl:
  256. proc macperl {} {
  257.     global macperlPath
  258.     set name [checkRunning MacPerl McPL macperlPath]
  259.     if {[string length $name]} {
  260.         switchTo "MacPerl"
  261.     } else {
  262.         alertnote "Couldn't run MacPerl"
  263.     }
  264. }
  265.  
  266. #############################################################################
  267. #
  268. proc perlOpenFile {menu name} {
  269.     set filename [macperlFolder]$name
  270.     if {[file exists $filename]} {
  271.         edit $filename
  272.     } else {
  273.         alertnote "That file doesn't exist yet"
  274.     }
  275. }
  276.  
  277. #############################################################################
  278. # Get a script file to run under MacPerl:
  279. #
  280. proc runAFile {} {
  281.     if {! [catch {getfile "Select a Perl script:"} path]} {
  282.             perlExecuteFile $path
  283.     }
  284. }
  285.  
  286. #############################################################################
  287. # Tell MacPerl to run a script file:
  288. #
  289. proc ExecuteFile {path} {
  290.     global macperlPath
  291.     if {[string length $path]} {
  292.         set name [checkRunning MacPerl McPL macperlPath]
  293.         if {[string length $name]} {
  294.             dosc -c 'McPL' -r -f $path
  295.             switchTo "MacPerl"
  296.         } else {
  297.             alertnote "Couldn't run MacPerl"
  298.         }
  299.     } else {
  300.             alertnote "No file specified to execute"
  301.     }
  302. }
  303.  
  304. #############################################################################
  305. # Run the buffer as a MacPerl script:
  306. #
  307. proc runTheBuffer {} {
  308.     perlExecuteScript [getText 0 [maxPos]]
  309. }
  310.  
  311. #############################################################################
  312. # Run the selection as a MacPerl script:
  313. # (No special arrangements are made to provide input or capture the output)
  314. proc runTheSelection {} {
  315.     completeSelection
  316.     perlExecuteScript [getSelect]
  317. }
  318.  
  319. #############################################################################
  320. # Run a MacPerl script file.
  321. # (No special arrangements are made to provide input or capture the output)
  322. proc perlExecuteFile {fname} {
  323.     set fd [open $fname "r"]
  324.     perlExecuteScript [read $fd]
  325.     close $fd
  326. }
  327.  
  328. #############################################################################
  329. # Run a MacPerl script, passed explicitly as a string:
  330. # (No special arrangements are made to provide input or capture the output)
  331. proc perlExecuteScript {script} {
  332.     global macperlPath
  333.     if {$script != ""} {
  334.         set name [checkRunning MacPerl McPL macperlPath]
  335.         if {[string length $name]} {
  336.             dosc -c 'McPL' -r -s $script
  337.             switchTo "MacPerl"
  338.        } else {
  339.             alertnote "Couldn't run MacPerl"
  340.        }
  341.     } else {
  342.         alertnote "Empty script"
  343.     }
  344. }
  345.  
  346. #############################################################################
  347. # Run a Perl script filter selected from the menu:
  348. #
  349. proc perlExecuteFilter {menu name} {
  350.     global perlFilterPath
  351.     set path $perlFilterPath($menu:$name)
  352. #    set path [scriptFolder]$name
  353.     set coreScript [readFile $path]
  354.     if {$coreScript != -1} {
  355.         set script [wrapFilterScript $coreScript]
  356.         filterThruMacperl $script
  357.     } else {
  358.         alertnote "Couldn't read the script file : $path"
  359.         return
  360.     }
  361. }
  362.  
  363. #############################################################################
  364. # Ask for a file containing a Perl script to use as a filter:
  365. #
  366. proc selectAFile {} {
  367.     if {! [catch {getfile "Select a MacPerl script"} path]} {
  368.         set coreScript [readFile $path]
  369.         if {$coreScript != -1} {
  370.             set script [wrapFilterScript $coreScript]
  371.             filterThruMacperl $script
  372.         } else {
  373.             alertnote "Couldn't read the script file : $path"
  374.             return
  375.         }
  376.     }
  377. }
  378.  
  379. #############################################################################
  380. # Ask for an Alpha buffer containing a Perl script to use as a filter:
  381. #
  382. proc selectABuffer {} {
  383.     set windows [winNames]
  384.     if {[llength $windows] > 1} {
  385.         set current [lindex $windows 0]
  386.         set name [listpick [lsort $windows]]
  387.         if {[string length $name]} {
  388.             bringToFront $name
  389.             set coreScript [getText 0 [maxPos]]
  390.             if {[string length $coreScript]} {
  391.                 set script [wrapFilterScript $coreScript]
  392.                 bringToFront $current
  393.                 filterThruMacperl $script
  394.             } else {
  395.                 bringToFront $current
  396.             }
  397.            }
  398.     }
  399. }
  400.  
  401. #############################################################################
  402. # Filter selection through a Perl script:
  403. # bugs:  If the script contains an existing !/bin/perl line, then it
  404. #        should be removed, or preferably used instead of my own new line.
  405. #
  406. proc filterThruMacperl {script} {
  407.     global macperlPath perlOverwrite perlUsebuffer
  408.  
  409.     set name [checkRunning MacPerl McPL macperlPath]
  410.     if {[string length $name]} {
  411.         writeStdin
  412.         writeStdout
  413.         dosc -c 'McPL' -t 0 -s $script
  414.     } else {
  415.         alertnote "Couldn't run MacPerl"
  416.     }
  417.     
  418.     if {!$perlOverwrite} new
  419.     if {$perlUsebuffer} {
  420.         pasteStdout 0 [maxPos]
  421.     } else {
  422.         pasteStdout [getPos] [selEnd]
  423.     }
  424. }
  425.  
  426. #############################################################################
  427. #  Take a Perl script and add commands to take the file STDIN as standard
  428. #  input and STDOUT as standard output.  This allows scripts written as
  429. #  Unix command-line filters to be used in the (non-MPW) Mac environment as
  430. #  text filters.
  431. #
  432. proc wrapFilterScript {coreScript} {
  433.  
  434.     set filterHead "#!/usr/bin/perl\n"
  435.     append filterHead "\$macperlDir = \"[macperlFolder]\" ;\n"
  436.     append filterHead "open(STDIN, \"<[stdinPath]\" ) ;\n"
  437.     append filterHead "open(STDOUT, \">[stdoutPath]\" ) ;\n"
  438.     append filterHead "@ARGV = (\"[stdinPath]\") ;\n"
  439.     append filterHead "select(STDOUT) ;\n\n"
  440.     
  441.     set filterTail "close STDIN ;\nclose STDOUT ;\n"
  442.         
  443.     set script $filterHead
  444.     append script $coreScript
  445.     append script $filterTail
  446.     
  447.     writeScript $script
  448.     return $script
  449. }        
  450.  
  451. #############################################################################
  452. #  Paste the text of the file STDOUT in place of the current selection.
  453. #
  454. proc pasteStdout {from to} {
  455.     set result [readFile [stdoutPath]]
  456.     if {$result != -1} {
  457.         deleteText $from $to
  458.         insertText $result
  459.         shrinkLow
  460.         goto 0
  461.     } else {
  462.         alertnote "Couldn't find the output file : STDOUT"
  463.     }
  464. }    
  465. #        replaceText [getPos] [selEnd] $result
  466.  
  467. #############################################################################
  468. #  Extend the current selection to encompass complete lines.
  469. #
  470. proc completeSelection {} {
  471.     global perlUsebuffer
  472.     if {$perlUsebuffer} {
  473.         set start 0
  474.         set end [maxPos]
  475.     } else {
  476.         set start [lineStart [getPos]]
  477.         set end [nextLineStart [expr [selEnd]-1]]
  478.     }
  479.     if {$end == $start} {set end [nextLineStart [selEnd]]}
  480.     select $start $end
  481. }
  482.  
  483. #############################################################################
  484. #
  485. proc writeStdin {} {
  486.     completeSelection
  487.     set tmpfid [open [stdinPath] "w+"]
  488.     puts $tmpfid [getSelect] 
  489.     close $tmpfid
  490. }
  491.  
  492. proc writeStdout {} {
  493.     completeSelection
  494.     set tmpfid [open [stdoutPath] "w+"]
  495.     puts $tmpfid [getSelect] 
  496.     close $tmpfid
  497. }
  498.  
  499. proc writeScript {script} {
  500.     set tmpfid [open [scriptPath] "w+"]
  501.     puts $tmpfid $script 
  502.     close $tmpfid
  503. }
  504.  
  505. #############################################################################
  506. #
  507. proc readFile {fileName} {
  508.     if {[file exists $fileName] && [file readable $fileName]} {
  509.        set fileid [open $fileName "r"]
  510.        set contents ""
  511.        while {[gets $fileid nextLine] != -1} {
  512.           append contents $nextLine "\n"
  513.        }
  514.        close $fileid
  515.        return $contents
  516.     } else {
  517.        return -1
  518.     }
  519. }
  520.